#include "Location.h"
#include <iostream>
#include <algorithm>
#include <functional>

#include "../Characters/Character.h"
#include "../Characters/Wizard.h"
#include "../Characters/Barbarian.h"
#include "../Characters/Paladin.h"


Location::Location()
{
	initialize("empty" , "You see... nothing");
}

Location::~Location()
{
	for(size_t i = 0 ; i < items.size() ; i++)
	{
		delete items[i];
	}
	items.clear();
	for(size_t i = 0 ; i < loc_characters.size() ; i++)
	{
		delete loc_characters[i];
	}
	loc_characters.clear();
	player = NULL;
}

std::string Location::get_description()
{
	return loc_description;
}

std::vector<neighbor> Location::get_neighbors()
{
	return loc_neighbors;
}

std::vector<Item*> Location::get_items()
{
	return items;
}

void Location::add_item(Item* item)
{
	items.push_back(item);
}

std::string Location::get_name()
{
	return loc_name;
}

void Location::initialize(std::string name, std::string description)
{
	loc_name = name;
	loc_description = description;
}

void Location::add_neighbor(std::string dir, Location* new_neighbor, progress p)
{
	neighbor n = {dir, new_neighbor, p};
	loc_neighbors.push_back(n);
}

void Location::add_character(Character * character)
{
	loc_characters.push_back(character);
}

void Location::set_visited(bool visited)
{
	loc_visited = visited;
}

void Location::remove_character(Character * character)
{
	loc_characters.erase(std::remove(loc_characters.begin(), loc_characters.end(), character), loc_characters.end());
}

void Location::remove_item(Item * item)
{
	items.erase(std::remove(items.begin(), items.end(), item), items.end());
}

void Location::print_menu()
{
	speak(std::string("You are in ") + get_name() + std::string("."));
}

Location* Location::start_battle(bool playerattack, Character * enemy)
{
	bool ranAway = enemy->battle(player, playerattack); 
	if(player->getHP() != 0){
		if(!ranAway){
			return this;
		}
		else{
			std::vector<neighbor> runTo;
			for(size_t j = 0; j < loc_neighbors.size(); j++){
				if(loc_neighbors[j].location->is_visited()){
					runTo.push_back(loc_neighbors[j]);
				}
			}
			if(runTo.size() != 0){
				int run = rand() % runTo.size();
				std::string say = "You ran in the " ;
				say += runTo[run].direction;
				say += " direction";
				speak(say);
				runTo[run].location->set_player(player);
				player = NULL;
				return runTo[run].location;
			}
			else{
				speak("I have no where to run!", player);
				return this;
			}
		}
	}
	else{
		speak("you have died");
		return NULL;
	}
}

Location* Location::run()
{
	print_menu();
	for(size_t i = 0 ; i < loc_characters.size() ; i++ )
	{
		if(loc_characters[i]->isHostile() && loc_characters[i]->getHP()>0)
		{
			return start_battle(false, loc_characters[i]);
		}
	}
	set_visited(true);
	Location *ret = this;
	while(ret == this)
	{
		std::vector<std::string> input = read(get_alternatives(), "What would you like to do? (help for options)");
		ret = create_reaction(input);
		if(player != NULL && player->achieved(KILL_GANDALF))
		{
			break;
		}
	}
	return ret;
}

std::vector<cmdalt> Location::get_alternatives()
{
	std::vector<cmdalt> alternatives;
		{
		// go direction
		cmdalt alt;
		alt.first = "go";
		std::vector<std::string> sec;
		for(size_t i = 0; i < loc_neighbors.size() ; i++)
		{
			sec.push_back(loc_neighbors[i].direction);
		}
		alt.second = sec;
		alternatives.push_back(alt);
	}
	{
		// exit
		cmdalt alt;
		alt.first = "exit";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// help
		cmdalt alt;
		alt.first = "help";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// sleep
		cmdalt alt;
		alt.first = "sleep";
		alt.second = {};
		alternatives.push_back(alt);
	}
	{
		// look around
		cmdalt alt;
		alt.first = "look";
		alt.second = {"around"};
		alternatives.push_back(alt);
	}
	{
		// check status
		cmdalt alt;
		alt.first = "check";
		alt.second = {"status", "equipment"};
		alternatives.push_back(alt);
	}
	{
		// talk [char]
		cmdalt alt;
		alt.first = "talk";
		std::vector<std::string> sec;
		for(size_t i = 0; i < loc_characters.size();i++){
			if(loc_characters[i]->getHP() > 0){
				sec.push_back(loc_characters[i]->name);
			}
		}
		alt.second = sec;
		if(alt.second.size() > 0){
			alternatives.push_back(alt);
		}
	}
	{
		// attack [char]
		cmdalt alt;
		alt.first = "attack";
		std::vector<std::string> sec;
		for(size_t i = 0; i < loc_characters.size();i++){
			if(loc_characters[i]->getHP() > 0){
				sec.push_back(loc_characters[i]->name);
			}
		}
		alt.second = sec;
		if(alt.second.size() > 0){
			alternatives.push_back(alt);
		}
	}
	{
		// loot [char]
		cmdalt alt;
		alt.first = "loot";
		std::vector<std::string> sec;
		for(size_t i = 0; i < loc_characters.size();i++){
			if(loc_characters[i]->getHP() <= 0){
				sec.push_back(loc_characters[i]->name);
			}
		}
		alt.second = sec;
		if(alt.second.size() > 0){
			alternatives.push_back(alt);
		}
	}
	{
		// loot [char]
		cmdalt alt;
		alt.first = "pick up";
		std::vector<std::string> sec;
		for(size_t i = 0; i < items.size();i++){
			sec.push_back(items[i]->getName());
		}
		alt.second = sec;
		if(alt.second.size() > 0){
			alternatives.push_back(alt);
		}
	}
	return alternatives;
}

Location* Location::create_reaction(std::vector<std::string> input)
{
	if(input.size() == 0)
	{
		return this; // ??
	}
	if(input.size() == 1)
	{
		if(input[0].compare("exit") == 0)
		{
			return NULL;
		}
		if(input[0].compare("help") == 0)
		{
			printAlternatives(get_alternatives());
		}
		if(input[0].compare("sleep") == 0)
		{
			bool sleepable = sleep(); 
			if(sleepable){
				player->sleep();
			}
			else{
				speak("I can not sleep here, I have to go inside.", player);
			}
		}
		
	}
	if(input.size() == 2){
		if(input[0].compare("go") == 0)
		{
			for(size_t i = 0; i < loc_neighbors.size() ; i++ )
			{
				if(loc_neighbors[i].direction.compare(input[1]) == 0)
				{
					speak(input);
					if(loc_neighbors[i].required == NONE || player->achieved(loc_neighbors[i].required))
					{
						loc_neighbors[i].location->set_player(player);
						player = NULL;
						return loc_neighbors[i].location;
					}
					else
					{
						speak(std::string("You cannot go ") + loc_neighbors[i].direction);
					}
				}
			}
		}
		if(input[0].compare("check") == 0){
			if(input[1].compare("status") == 0){
				player->getStatusAllAttributes();
			}
			if(input[1].compare("equipment") == 0){
				player->printEquipment();
			}
		}
		if(input[0].compare("look") == 0 && input[1].compare("around") == 0) // TODO change to inspect
		{
			speak(loc_name);
			speak(loc_description);
			std::string dir = "You see exits at ";
			for(size_t i = 0; i < loc_neighbors.size() ; i++ )
			{
				dir += loc_neighbors[i].direction;
				if(i < loc_neighbors.size() - 1){
					dir += ", ";
			}
			speak(dir);
			if(loc_characters.size() > 0){
				std::string chara = "You see ";
				for(size_t i = 0; i < loc_characters.size() ; i++ )
				{
					if(loc_characters[i]->getHP() > 0){
						chara += loc_characters[i]->name;
						chara += " the ";
						chara += loc_characters[i]->race;
						chara += "(";
						chara += loc_characters[i]->getType();
						chara += ")";
					}
					else{
						chara += "the dead body of ";
						chara += loc_characters[i]->name;
					}
					if(i < loc_characters.size() - 1){
						chara += ", ";
					}
				}
				speak(chara);
			}
			std::string item;
			if(items.size() > 0){
				std::string item = "You see ";
				item += std::to_string(items.size());
				if(items.size > 1)
				{
					item += " items: ";
				}
				else
				{
					item += " items: ";	
				}
				for(int i = 0; i < (int)items.size();i++){
					item += items[i]->getName();
					if(i < (int)items.size() -2){
						item += ", ";
					}
					if(i == (int)items.size() -2){
						item += " and ";
					}
					if(i == (int)items.size() -1){
						item += ".";
					}
				}
				speak(item);
			}
			return this;
		}
		if(input[0].compare("talk") == 0){
			for(size_t i = 0; i < loc_characters.size(); i++){
				if(input[1].compare(loc_characters[i]->name) == 0){
					loc_characters[i]->talk(player);
					return this;
				}
			}
		}
		if(input[0].compare("attack") == 0){
			for(size_t i = 0; i < loc_characters.size(); i++){
				if(input[1].compare(loc_characters[i]->name) == 0){
					return start_battle(true, loc_characters[i]);
				}
			}
		}
		if(input[0].compare("loot") == 0){
			for(size_t i = 0; i < loc_characters.size(); i++){
				if(input[1].compare(loc_characters[i]->name) == 0){
					loc_characters[i]->loot(player);
					return this;
				}
			}
		}
		if(input[0].compare("pick up") == 0){
			for(size_t i = 0; i < items.size(); i++){
				if(input[1].compare(items[i]->getName()) == 0){
					if(player->equipItem(*items[i]))
					{
						remove_item(items[i]);
					}
					else{
						speak("I think i will leave it for now", player);
					}
					return this;
				}
			}
		}
	}
	return this;
}

void Location::set_player(Character * p){
	player = p;
}

bool Location::is_visited()
{
	return loc_visited;
}
